package com.winning.pmph.service;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.winning.pmph.entity.*;
import com.winning.pmph.mapper.ExamOutlineMapper;
import com.winning.pmph.utils.Const;
import com.winning.pmph.vo.ExamOutlineParamVo;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author admin
 */
@Service
@Transactional(rollbackFor = RuntimeException.class)
public class ExamOutlineService extends ServiceImpl<ExamOutlineMapper, ExamOutline> {

    @Resource
    private ExamOutlineTagService examOutlineTagService;
    @Resource
    private ExamOutlineDetailService examOutlineDetailService;
    @Resource
    private ExamBookService examBookService;
    @Resource
    private ExerciseGroupLibService exerciseGroupLibService;
    @Resource
    private ExamKnowledgeService examKnowledgeService;

    public void uploadAndInsertFile(String toParseFilePath, ExamOutline examOutline) {
        File file = new File(toParseFilePath);
        // 用于校验重复节点, key 是 当前节点名称+所有父级节点名称
        Map<String, ExamOutlineTag> checkMap = new HashMap<>();
        // 组装要入库的集合数据
        List<ExamOutlineTag> tagList = new ArrayList<>();
        List<ExamOutlineDetail> detailList = new ArrayList<>();
        try (XSSFWorkbook workbook = new XSSFWorkbook(file)) {
            XSSFSheet sheet = workbook.getSheetAt(0);
            examOutline.setName(sheet.getSheetName());
            int topSort = 0;
            for (int j = 3; j <= sheet.getLastRowNum(); j++) {
                XSSFRow row = sheet.getRow(j);
                if (Objects.isNull(row)) {
                    throw new RuntimeException("文件: " + file.getName() + ", 页签: " + sheet.getSheetName() + ", 第" + (j + 1) + "行为空行");
                }
                // 每个单元格都不能为空
                Objects.requireNonNull(row.getCell(0), "文件: " + file.getName() + ", 页签: " + sheet.getSheetName() + ", 第" + (j + 1) + "行, 单元不能为空");
                Objects.requireNonNull(row.getCell(1), "文件: " + file.getName() + ", 页签: " + sheet.getSheetName() + ", 第" + (j + 1) + "行, 细目不能为空");
                Objects.requireNonNull(row.getCell(3), "文件: " + file.getName() + ", 页签: " + sheet.getSheetName() + ", 第" + (j + 1) + "行, 要求不能为空");
                Objects.requireNonNull(row.getCell(4), "文件: " + file.getName() + ", 页签: " + sheet.getSheetName() + ", 第" + (j + 1) + "行, 科目不能为空");
                // 顶级节点名称
                String topTagName = row.getCell(0).getStringCellValue().trim();
                // 所有子节点
                String allSubTagName = row.getCell(1).getStringCellValue().trim();
                // 根节点重复时，子节点也要做重复校验, 一级节点
                ExamOutlineTag topTag = checkMap.get(topTagName);
                if (topTag != null) {
                    subTagAndDetailList(new ExamOutlineParamVo(
                            topTag.getId(),
                            topTagName,
                            allSubTagName,
                            tagList,
                            detailList,
                            checkMap,
                            row,
                            examOutline));
                    continue;
                }
                String rootId = IdWorker.getIdStr();
                //根节点   （不重复时，子节点也不做重复校验）
                ExamOutlineTag rootNode = getTag(rootId, topTagName, null, 1);
                rootNode.setSort(topSort++);
                tagList.add(rootNode);
                checkMap.put(topTagName, rootNode);
                // 子节点
                subTagAndDetailList(new ExamOutlineParamVo(rootId, topTagName,
                        allSubTagName, tagList, detailList,
                        checkMap, row, examOutline));
            }

            // 添加排序序号   sort 从0开始,方便前端判断上移,按钮是否显示
            LinkedHashMap<String, List<ExamOutlineTag>> linkedHashMap = tagList.stream().filter(item -> item.getLevel() != 1)
                    .collect(Collectors.groupingBy(ExamOutlineTag::getParentId, LinkedHashMap::new,
                            Collectors.toList()));
            linkedHashMap.values().forEach(itemList -> {
                for (int i = 0; i < itemList.size(); i++) {
                    itemList.get(i).setSort(i);
                }
            });
            List<ExamOutlineTag> allTagList = linkedHashMap.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
            allTagList.addAll(tagList.stream().filter(item -> item.getLevel() == 1).collect(Collectors.toList()));

            allTagList.forEach(item -> {
                item.setExamOutlineId(examOutline.getId());
                item.setCreatedBy(examOutline.getUpdatedBy());
                item.setUpdatedBy(examOutline.getUpdatedBy());
            });

            LinkedHashMap<String, List<ExamOutlineDetail>> detailMap = detailList.stream()
                    .collect(Collectors.groupingBy(ExamOutlineDetail::getExamOutlineTagId, LinkedHashMap::new,
                            Collectors.toList()));

            detailMap.values().forEach(itemList -> {
                for (int i = 0; i < itemList.size(); i++) {
                    itemList.get(i).setSort(i);
                }
            });

            examOutlineTagService.saveBatch(allTagList);
            List<ExamOutlineDetail> examOutlineDetails = detailMap.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
            examOutline.setDetailList(examOutlineDetails);
            examOutlineDetailService.saveBatch(examOutlineDetails);
            // 设置单元数  一级标签数量
            examOutline.setUnitNum((int) allTagList.stream().filter(item -> item.getLevel() == 1).count());
            // 设置要点数
            examOutline.setDetailNum(detailList.size());
            // 设置分类数(最末级)
            examOutline.setTagNum((int) allTagList.stream().filter(item -> !allTagList.stream()
                    .map(ExamOutlineTag::getParentId).collect(Collectors.toSet()).contains(item.getId())).count());

            examOutline.setImportStatus(Const.IMPORT_STATUS_SUCCESS);

        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        } finally {
            FileUtils.deleteQuietly(file);
        }
    }

    private void subTagAndDetailList(ExamOutlineParamVo examOutlineParamVo) {
        String[] values = examOutlineParamVo.getAllSubTagName().split("/");
        // 根节点的id作为父id
        String parentId = examOutlineParamVo.getTopTagId();
        Integer level = 2;
        for (int i = 0; i < values.length; i++) {
            String s = values[i];
            ExamOutlineTag vo = examOutlineParamVo.getCheckMap().get(s + examOutlineParamVo.getTopTagName() + getAllParentTagName(values, i));
            if (vo != null) {
                parentId = vo.getId();
                level++;
                if (i == values.length - 1) {
                    examOutlineParamVo.getDetailList()
                            .add(getOutLineDetail(examOutlineParamVo.getExamOutline(), vo.getId(),
                                    examOutlineParamVo.getRow(), examOutlineParamVo.getDetailList()));
                }
                continue;
            }
            String id = IdWorker.getIdStr();
            ExamOutlineTag sonNode = getTag(id, s, parentId, level);
            examOutlineParamVo.getTagList().add(sonNode);
            examOutlineParamVo.getCheckMap().put(s + examOutlineParamVo.getTopTagName() + getAllParentTagName(values, i), sonNode);
            parentId = id;
            level++;
            // 当前id作为下个节点的父Id
            if (i == values.length - 1) {
                examOutlineParamVo.getDetailList().add(getOutLineDetail(examOutlineParamVo.getExamOutline(),
                        id, examOutlineParamVo.getRow(), examOutlineParamVo.getDetailList()));
            }
        }
    }

    private String getAllParentTagName(String[] values, int i) {
        String allParentTagName = "";
        for (int j = 0; j < i; j++) {
            allParentTagName += values[j];
        }
        return allParentTagName;
    }

    private ExamOutlineTag getTag(String rootId,
                                  String name,
                                  String parentId,
                                  Integer level) {
        ExamOutlineTag rootNode = new ExamOutlineTag();
        rootNode.setId(rootId);
        rootNode.setName(name);
        rootNode.setParentId(parentId);
        rootNode.setLevel(level);
        return rootNode;
    }

    public ExamOutlineDetail getOutLineDetail(ExamOutline examOutline, String tagId, XSSFRow row, List<ExamOutlineDetail> detailList) {
        ExamOutlineDetail examOutlineDetail = new ExamOutlineDetail();
        examOutlineDetail.setExamOutlineId(examOutline.getId());
        examOutlineDetail.setExamOutlineTagId(tagId);
        examOutlineDetail.setName(((Objects.isNull(row.getCell(2)) || StringUtils.isBlank(row.getCell(2).getStringCellValue())) ?
                row.getCell(1).getStringCellValue().substring(row.getCell(1).getStringCellValue().lastIndexOf("/") + 1) :
                row.getCell(2).getStringCellValue()).trim());
        examOutlineDetail.setRequireValue(row.getCell(3).getStringCellValue().trim());
        examOutlineDetail.setSubjectValue(row.getCell(4).getStringCellValue().trim());
        examOutlineDetail.setSource(0);
        examOutlineDetail.setSort(detailList.size());
        examOutlineDetail.setCreatedBy(examOutline.getUpdatedBy());
        examOutlineDetail.setUpdatedBy(examOutline.getUpdatedBy());
        return examOutlineDetail;
    }

    public void deleteById(List<String> idList) {
        examOutlineTagService.remove(Wrappers.lambdaQuery(ExamOutlineTag.class).in(ExamOutlineTag::getExamOutlineId, idList));
        examOutlineDetailService.remove(Wrappers.lambdaQuery(ExamOutlineDetail.class).in(ExamOutlineDetail::getExamOutlineId, idList));
        // 如果绑定的有考试指导用书,则不能删除
        List<ExamBook> examBookList = examBookService.lambdaQuery().in(ExamBook::getExamOutlineId, idList).list();
        if (CollectionUtils.isNotEmpty(examBookList)) {
            throw new RuntimeException("该大纲绑定了考试指导用书,不能删除");
        }
        this.removeByIds(idList);
    }

    public List<ExamOutlineTag> selectAllTagList(String examOutlineId) {
        List<ExamOutlineTag> allTagList = examOutlineTagService.lambdaQuery().eq(ExamOutlineTag::getExamOutlineId, examOutlineId)
                .orderByAsc(ExamOutlineTag::getSort).list();
        Map<String, ExamOutlineTag> id2item = allTagList.stream().collect(Collectors.toMap(ExamOutlineTag::getId, item -> item));
        for (ExamOutlineTag examOutlineTag : allTagList) {
            if (StringUtils.isBlank(examOutlineTag.getParentId())) {
                continue;
            } else {
                ExamOutlineTag parentNode = id2item.get(examOutlineTag.getParentId());
                if (CollectionUtils.isEmpty(parentNode.getChildren())) {
                    parentNode.setChildren(Lists.newArrayList());
                }
                parentNode.getChildren().add(examOutlineTag);
            }
        }
        return allTagList;
    }

    public void saveTag(ExamOutlineTag examOutlineTag) {
        // 新增分类时,对应的分类数也要更改
        boolean isUpdate = false;
        if (StringUtils.isBlank(examOutlineTag.getId())) {
            isUpdate = true;
        }
        examOutlineTagService.saveOrUpdate(examOutlineTag);
        if (isUpdate) {
            updateExamOutlineTagNum(examOutlineTag.getExamOutlineId());
        }
    }

    public void deleteTag(ExamOutlineTag examOutlineTag) {
        List<String> tagIdList = examOutlineTagService.getBaseMapper().selectSelfAndSubTag(examOutlineTag.getId());
        // 暂时不考虑超过1000的子目录
        List<ExamOutlineDetail> examOutlineDetailList = examOutlineDetailService.lambdaQuery().in(ExamOutlineDetail::getExamOutlineTagId, tagIdList).list();
        if (CollectionUtils.isNotEmpty(examOutlineDetailList)) {
            throw new RuntimeException("标签被要点引用,不能删除");
        }
        // 删除标签
        examOutlineTagService.remove(Wrappers.lambdaQuery(ExamOutlineTag.class).in(ExamOutlineTag::getId, tagIdList));
        // 后续标签sort--
        List<ExamOutlineTag> examOutlineTagList = examOutlineTagService.lambdaQuery().eq(ExamOutlineTag::getParentId, examOutlineTag.getParentId())
                .gt(ExamOutlineTag::getSort, examOutlineTag.getSort()).list();
        examOutlineTagList.forEach(item -> item.setSort(item.getSort() - 1));
        examOutlineTagService.updateBatchById(examOutlineTagList);
        // 新增分类时,对应的细目数也要更改
        updateExamOutlineTagNum(examOutlineTag.getExamOutlineId());
    }

    /**
     * 新增,删除分类时同时更新细目数
     *
     * @param examOutlineId
     */
    private void updateExamOutlineTagNum(String examOutlineId) {
        ExamOutline examOutline = getBaseMapper().selectById(examOutlineId);
        List<ExamOutlineTag> allTagList = examOutlineTagService.lambdaQuery().eq(ExamOutlineTag::getExamOutlineId, examOutlineId).list();
        // 设置细目数(最末级)
        examOutline.setTagNum((int) allTagList.stream().filter(item -> !allTagList.stream()
                .map(ExamOutlineTag::getParentId).collect(Collectors.toSet()).contains(item.getId())).count());
        updateById(examOutline);
    }

    public void tagMove(ExamOutlineParamVo examOutlineParamVo) {
        // 修改sort
        ExamOutlineTag curExamOutlineTag = examOutlineParamVo.getCurExamOutlineTag();
        ExamOutlineTag preExamOutlineTag = examOutlineParamVo.getPreExamOutlineTag();
        curExamOutlineTag.setSort(curExamOutlineTag.getSort() - 1);
        preExamOutlineTag.setSort(curExamOutlineTag.getSort() + 1);
        examOutlineTagService.updateById(curExamOutlineTag);
        examOutlineTagService.updateById(preExamOutlineTag);
    }

    public Page<ExamOutlineDetail> selectDetailPage(ExamOutlineDetail examOutlineDetail) {
        // 1级,查询1级对应的所有要点,暂时不考虑子目录超过1000的情况
        LambdaQueryChainWrapper<ExamOutlineDetail> wrapper = examOutlineDetailService.lambdaQuery().eq(ExamOutlineDetail::getExamOutlineId, examOutlineDetail.getExamOutlineId())
                .eq(StringUtils.isNotBlank(examOutlineDetail.getExamOutlineTagId()) && examOutlineDetail.getTagLevel() != 1,
                        ExamOutlineDetail::getExamOutlineTagId, examOutlineDetail.getExamOutlineTagId())
                .in(examOutlineDetail.getTagLevel() == 1, ExamOutlineDetail::getExamOutlineTagId, examOutlineTagService.getBaseMapper().selectSelfAndSubTag(examOutlineDetail.getExamOutlineTagId()))
                .like(StringUtils.isNotBlank(examOutlineDetail.getName()), ExamOutlineDetail::getName, examOutlineDetail.getName())
                .like(StringUtils.isNotBlank(examOutlineDetail.getRequireValue()), ExamOutlineDetail::getRequireValue, examOutlineDetail.getRequireValue())
                .like(StringUtils.isNotBlank(examOutlineDetail.getSubjectValue()), ExamOutlineDetail::getSubjectValue, examOutlineDetail.getSubjectValue())
                .orderByAsc(ExamOutlineDetail::getSort).orderByAsc(ExamOutlineDetail::getId);

        if (CollectionUtils.isNotEmpty(examOutlineDetail.getDateRange())) {
            wrapper.ge(ExamOutlineDetail::getCreatedTime, examOutlineDetail.getDateRange().get(0));
            wrapper.le(ExamOutlineDetail::getCreatedTime, examOutlineDetail.getDateRange().get(1));
        }

        if (Objects.nonNull(examOutlineDetail.getKnowledgeNumStartIndex())) {
            wrapper.ge(ExamOutlineDetail::getKnowledgeNum, examOutlineDetail.getKnowledgeNumStartIndex());
            wrapper.le(ExamOutlineDetail::getKnowledgeNum, examOutlineDetail.getKnowledgeNumEndIndex());
        }
        if (Objects.nonNull(examOutlineDetail.getExerciseNumStartIndex())) {
            wrapper.ge(ExamOutlineDetail::getExerciseNum, examOutlineDetail.getExerciseNumStartIndex());
            wrapper.le(ExamOutlineDetail::getExerciseNum, examOutlineDetail.getExerciseNumEndIndex());
        }
        return wrapper.page(new Page<>(examOutlineDetail.getCurrentPage(), examOutlineDetail.getPageSize()));
    }

    public int selectDetailName(ExamOutlineDetail examOutlineDetail) {
        List<ExamOutlineDetail> list = examOutlineDetailService.lambdaQuery().eq(ExamOutlineDetail::getExamOutlineId, examOutlineDetail.getExamOutlineId())
                .eq(ExamOutlineDetail::getExamOutlineTagId, examOutlineDetail.getExamOutlineTagId())
                .ne(StringUtils.isNotBlank(examOutlineDetail.getId()), ExamOutlineDetail::getId, examOutlineDetail.getId())
                .list();
        if (list.stream().filter(item -> item.getName().equals(examOutlineDetail.getName())).count() > 0) {
            throw new RuntimeException("要点名已存在");
        }
        return list.size();
    }

    public void saveDetail(ExamOutlineDetail examOutlineDetail) {
        boolean isUpdate = false;
        if (StringUtils.isBlank(examOutlineDetail.getId())) {
            isUpdate = true;
        }
        examOutlineDetailService.saveOrUpdate(examOutlineDetail);
        // 新增时,同时更新要点数
        if (isUpdate) {
            updateExamOutlineDetailNum(examOutlineDetail.getExamOutlineId());
        }
    }

    public void deleteDetailById(List<String> idList, String examOutlineTagId, String examOutlineId) {
        // 如果要点被知识和题库引用,则不能删除
        List<ExerciseGroupLib> groupLibList = exerciseGroupLibService.lambdaQuery().like(ExerciseGroupLib::getExamDetailIdArray, "[\"").list();
        groupLibList.forEach(groupLibItem -> {
            groupLibItem.getExamDetailIdArray().stream().forEach(detailIdItem -> {
                if (idList.contains(detailIdItem)) {
                    throw new RuntimeException("要点被题库引用,不能删除");
                }
            });
        });
        if (examKnowledgeService.lambdaQuery().in(ExamKnowledge::getOutlineDetailId, idList).count() > 0) {
            throw new RuntimeException("要点被考试指导用书的知识引用,不能删除");
        }
        // 因为无法保证顺序删除,删除后,这一分类下的所有要点重新排序
        examOutlineDetailService.removeByIds(idList);
        List<ExamOutlineDetail> detailList = examOutlineDetailService.lambdaQuery()
                .eq(ExamOutlineDetail::getExamOutlineTagId, examOutlineTagId)
                .orderByAsc(ExamOutlineDetail::getSort).list();
        for (int i = 0; i < detailList.size(); i++) {
            detailList.get(i).setSort(i);
        }
        examOutlineDetailService.updateBatchById(detailList);
        // 删除时,同时更新要点数
        updateExamOutlineDetailNum(examOutlineId);
    }

    private void updateExamOutlineDetailNum(String examOutlineId) {
        ExamOutline examOutline = getBaseMapper().selectById(examOutlineId);
        examOutline.setDetailNum(examOutlineDetailService.lambdaQuery().eq(ExamOutlineDetail::getExamOutlineId, examOutlineId).count());
        updateById(examOutline);
    }

    public void detailMove(ExamOutlineParamVo examOutlineParamVo) {
        // 修改sort
        ExamOutlineDetail curExamOutlineDetail = examOutlineParamVo.getCurExamOutlineDetail();
        ExamOutlineDetail preExamOutlineDetail = examOutlineParamVo.getPreExamOutlineDetail();
        curExamOutlineDetail.setSort(curExamOutlineDetail.getSort() - 1);
        preExamOutlineDetail.setSort(curExamOutlineDetail.getSort() + 1);
        examOutlineDetailService.updateById(curExamOutlineDetail);
        examOutlineDetailService.updateById(preExamOutlineDetail);
    }
}
